<template>
  <div class="map-toolbar">
    <img class="box" src="./point.png" @click="measure('Point')">
    <img class="box" src="./distance.png" @click="measure('LineString')">
    <img class="box" src="./area.png" @click="measure('Polygon')">
    <img class="box" src="./clear.png" @click="clearMeasure">
  </div>
</template>
<script>
import { reactive, toRefs, watch } from 'vue'
import { Feature } from 'ol'
import VectorSource from 'ol/source/Vector'
import VectorLayer from 'ol/layer/Vector'
import { Stroke, Style, Fill } from 'ol/style'
import CircleStyle from 'ol/style/Circle'
import { Draw } from 'ol/interaction'
import { getArea, getLength } from 'ol/sphere'
import { unByKey } from 'ol/Observable'
import { Overlay } from 'ol'
import { Point, LineString, Polygon } from 'ol/geom'
import { transform, fromLonLat, getTransform } from 'ol/proj'
import { styleList } from '@/utils/style.js'

// turf 用于简单的空间计算
import * as turf from '@turf/turf'
export default {
  props: {
    map: {
      type: Object,
      default: () => {}
    }
  },
  setup(props, { emit }) {
    const data = reactive({
      mapData: null,
      mapMouseMove: null,
      helpTooltipElement: null,
      feature: null,
      draw: null,
      listener: null,
      measureTooltipElement: null,
      measureTooltip: null,
      drawLayers: [],
      drawElements: [],
      buffer: null
    })

    watch(() => props.map, (map) => {
      data.mapData = map
      cpRPA.setDistanceFn(distance)
      cpRPA.setLatlng2PxFn(latlng2Px)
      cpRPA.setPx2LatlngFn(px2Latlng)

      data.buffer = drawPointBuffer().buffered
    })

    function distance(p1, p2) {
      const point1 = turf.point([parseFloat(p1.lng), parseFloat(p1.lat)])
      const point2 = turf.point([parseFloat(p2.lng), parseFloat(p2.lat)])
      return turf.distance(point1, point2, { units: 'meters' })
    }

    function latlng2Px(latlng) {
      const pixel = data.mapData.getPixelFromCoordinate(fromLonLat([latlng.lng, latlng.lat]))
      return {
        x: pixel[0],
        y: pixel[1]
      }
    }

    function px2Latlng(px) {
      const coord = transform(
        data.mapData.getCoordinateFromPixel(px),
        'EPSG:3857',
        'EPSG:4326'
      )
      return {
        lat: coord[1],
        lng: coord[0]
      }
    }

    /**
     * 绘制点的缓冲区
     * @param {*} lngLat
     * @param {*} range
     */
    const drawPointBuffer = (lngLat, range) => {
      var point = turf.point([119.7286, 32.0445])
      var buffered = turf.buffer(
        point,
        5000,
        { units: 'meters', steps: 64 }
      )
      const polygon = new Polygon([buffered.geometry.coordinates[0]])
      polygon.applyTransform(getTransform('EPSG:4326', 'EPSG:3857'))
      const source = new VectorSource({
        features: [new Feature(polygon)]
      })
      const layer = new VectorLayer({
        source: source,
        // 设置样式，边框和填充
        style: new Style({
          stroke: new Stroke({
            color: 'red',
            width: 5
          }),
          fill: new Fill({
            color: 'rgba(255, 0, 0, 0.5)'
          })
        })
      })

      data.mapData.addLayer(layer)
      return {
        layer,
        buffered
      }
    }

    /**
     * 如果第二个几何图形完全包含在第一个几何图形中，返回 true
     * @param {*} baseFeature
     * @param {*} proFeature
     */
    const boolContain = (baseFeature, proFeature) => {
      return turf.booleanContains(baseFeature, proFeature)
    }

    /**
     *  清除测量
     */
    const clearMeasure = () => {
      data.drawLayers.map((layer) => {
        if (layer) {
          data.mapData.removeLayer(layer)
        }
      })
      data.drawElements.map((overlay) => {
        if (overlay) {
          data.mapData.removeOverlay(overlay)
        }
      })

      data.drawLayers = []
      data.drawElements = []
    }

    /**
     *  测距
     */
    const measure = (measureType) => {
      // 清除上一个绘制
      clearMeasure()
      // 创建一个数据源,用于放置绘制过程中和绘制结束后的线段
      const source = new VectorSource()
      // 添加图层，用来放置数据源
      const layer = new VectorLayer({
        source: source,
        style: new Style({
          fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)'
          }),
          stroke: new Stroke({
            color: '#ffcc33',
            width: 4
          }),
          image: new CircleStyle({
            radius: 7,
            fill: new Fill({
              color: '#ffcc33'
            })
          })
        })
      })
      data.mapMouseMove = data.mapData.on('pointermove', (evt) => {
        if (evt.dragging) {
          return
        }
        let helpMsg = '单击选择起点'
        if (data.feature) {
          helpMsg = '双击结束选点'
        }
        data.helpTooltipElement.innerHTML = helpMsg
        data.helpTooltip.setPosition(evt.coordinate)
        data.helpTooltipElement.classList.remove('hidden')
      })

      data.mapData.getViewport().addEventListener('mouseout', () => {
        data.helpTooltipElement.classList.add('hidden')
      })

      data.draw = new Draw({
        source,
        // 测量方式 polygon 或者 lineString
        type: measureType,
        style: new Style({
          fill: new Fill({
            color: 'rgba(255, 255, 255, 0.2)'
          }),
          stroke: new Stroke({
            color: 'rgba(0, 0, 0, 0.5)',
            lineDash: [10, 10],
            width: 4
          }),
          image: new CircleStyle({
            radius: 5,
            stroke: new Stroke({
              color: 'rgba(0, 0, 0, 0.7)'
            }),
            fill: new Fill({
              color: 'rgba(255, 255, 255, 0.2)'
            })
          })
        })
      })

      // 开始进行绘制
      data.draw.on('drawstart', (evt) => {
        data.feature = evt.feature
        let tooltipCoord = evt.coordinate
        data.listener = data.feature.getGeometry().on('change', (evt) => {
          const geom = evt.target
          //   let output
          if (geom instanceof Polygon) {
            // output = formatArea(geom)
            tooltipCoord = geom.getInteriorPoint().getCoordinates()
          } else if (geom instanceof LineString) {
            // output = formatLength(geom)
            tooltipCoord = geom.getLastCoordinate()
          }

          //   data.measureTooltipElement.innerHTML = output
          data.measureTooltip.setPosition(tooltipCoord)
        })
      })

      // 绘制完成
      data.draw.on('drawend', (evt) => {
        data.measureTooltipElement.className = 'ol-tooltip ol-tooltip-static'
        data.measureTooltip.setOffset([0, -7])
        data.feature = null
        data.measureTooltipElement = null
        // createMeasureTooltip()
        data.mapData.removeInteraction(data.draw)
        unByKey(data.listener)
        unByKey(data.mapMouseMove)

        var feature = evt.feature
        var geometry = feature.getGeometry()
        var coordinate = geometry.getCoordinates()
        if (geometry instanceof Point) {
          const pt = turf.point(
            transform(
              coordinate,
              'EPSG:3857',
              'EPSG:4326'
            ))
          if (boolContain(data.buffer, pt)) {
            console.log('在范围内')
          } else {
            $message.error('超过机场覆盖范围!')
            clearMeasure()
            return false
          }
        } else if (geometry instanceof LineString) {
          const lngLats = []
          coordinate.map((item) => {
            lngLats.push(
              transform(
                item,
                'EPSG:3857',
                'EPSG:4326'
              ))
          })
          const line = turf.lineString(lngLats)
          if (boolContain(data.buffer, line)) {
            console.log('在范围内')
          } else {
            $message.error('超过机场覆盖范围!')
            clearMeasure()
            return false
          }
        } else if (geometry instanceof Polygon) {
          const lngLats = []
          const lngLats_cpRPA = []
          coordinate[0].map((item) => {
            const coord = transform(
              item,
              'EPSG:3857',
              'EPSG:4326'
            )
            lngLats.push(coord)
            lngLats_cpRPA.push(
              {
                lat: coord[1],
                lng: coord[0]
              }
            )
          })

          const poly = turf.polygon([lngLats])
          if (boolContain(data.buffer, poly)) {
            var polyline = cpRPA.setOptions({
              polygon: lngLats_cpRPA,
              rotate: 0,
              space: 100
            })

            drawPolyline(formatPolyline(polyline))
          } else {
            $message.error('超过机场覆盖范围!')
            clearMeasure()
            return false
          }
        }
      })

      createHelpTooltip()
      createMeasureTooltip()
      data.mapData.addLayer(layer)
      data.drawLayers.push(layer)
      data.mapData.addInteraction(data.draw)
    }

    const drawPolyline = (lngLats) => {
      const route = new LineString(lngLats)
      const routeFeature = new Feature({
        type: 'route',
        geometry: route
      })
      const vectorLayer = new VectorLayer({
        source: new VectorSource({
          features: [routeFeature]
        }),
        style: function(feature) {
          return styleList[feature.get('type')]
        }
      })
      data.mapData.addLayer(vectorLayer)
    }

    const formatPolyline = (coordinate) => {
      const lngLats = []
      coordinate.map((item) => {
        lngLats.push(fromLonLat([parseFloat(item.lng), parseFloat(item.lat)]))
      })
      return lngLats
    }

    // 计算长度
    const formatLength = (line) => {
      // ol自带的计算线段长度方法
      const length = getLength(line)
      let output
      // 长度大于100米单位为km, 否则为m
      if (length > 100) {
        output = Math.round((length / 1000) * 100) / 100 + ' ' + 'km'
      } else {
        output = Math.round(length * 100) / 100 + ' ' + 'm'
      }
      return output
    }

    // 计算面积
    const formatArea = (polygon) => {
      const proj = data.mapData.getView().getProjection()
      const area = getArea(polygon, { projection: proj })
      let output
      if (area > 10000) {
        output = Math.round((area / 1000000 * 100) / 100) + ' ' + 'km<sup>2</sup>'
      } else {
        output = Math.round(area * 100) / 100 + ' ' + 'm<sup>2</sup>'
      }
      return output
    }

    const createMeasureTooltip = () => {
      if (data.measureTooltipElement) {
        data.measureTooltipElement.parentNode.removeChild(data.measureTooltipElement)
      }
      data.measureTooltipElement = document.createElement('div')
      data.measureTooltipElement.className = 'ol-tooltip ol-tooltip-measure'
      data.measureTooltip = new Overlay({
        element: data.measureTooltipElement,
        offset: [0, -15],
        positioning: 'bottom-center',
        stopEvent: false,
        insertFirst: false
      })
      data.drawElements.push(data.measureTooltip)
      data.mapData.addOverlay(data.measureTooltip)
    }

    const createHelpTooltip = () => {
      if (data.helpTooltipElement) {
        data.helpTooltipElement.parentNode.removeChild(data.helpTooltipElement)
      }
      data.helpTooltipElement = document.createElement('div')
      data.helpTooltipElement.className = 'ol-tooltip hidden'
      data.helpTooltip = new Overlay({
        element: data.helpTooltipElement,
        offset: [15, 0],
        positioning: 'center-left'
      })
      data.mapData.addOverlay(data.helpTooltip)
    }

    return {
      ...toRefs(data),
      measure,
      clearMeasure
    }
  }
}
</script>

<style scoped lang="scss">
.map-toolbar {
  width: 32px;
  height: 150px;
  position: absolute;
  left: 10px;
  bottom: 10px;
  background-color: #fff;
  .box {
    width: 32px;
    height: 32px;
    background-size: 100% 100%;
    display: inline-block;
    cursor: pointer;
  }

}

::v-deep(.hidden) {
    display: none;
}

::v-deep(.ol-tooltip) {
    position: relative;
    background: rgba(0, 0, 0, 0.5);
    border-radius: 4px;
    color: white;
    padding: 4px 8px;
    opacity: 0.7;
    white-space: nowrap;
    font-size: 12px;
    cursor: default;
    user-select: none;
}

::v-deep(.ol-tooltip-measure) {
opacity: 1;
font-weight: bold;
}

::v-deep(.ol-tooltip-static) {
background-color: #ffcc33;
color: black;
border: 1px solid white;
}

::v-deep(.ol-tooltip-measure:before),
::v-deep(.ol-tooltip-static:before) {
border-top: 6px solid rgba(0, 0, 0, 0.5);
border-right: 6px solid transparent;
border-left: 6px solid transparent;
content: "";
position: absolute;
bottom: -6px;
margin-left: -7px;
left: 50%;
}

::v-deep(.ol-tooltip-static:before) {
border-top-color: #ffcc33;
}

</style>
